#include "tools/Wings.h"
#include <sys/stat.h>
void Nlwingsing::run(){
    QMutex lock;
    lock.lock();
    Function_Flag funcType = property("function-type").value<Function_Flag>();
    if (funcType == FUNCTION_FIND)
    {
        int count = 0;
        QList<QString> fishhook;
        foreach (QVariant path, infomap[runningKey]["matching-path"])
        {
            QFileInfo _f(path.toString().simplified());
            QList<QVariant> modes = infomap[runningKey]["matching-mode"];
            MatchingMode mc_mode;
            if (modes.count() > count)
            {
                mc_mode = infomap[runningKey]["matching-mode"][count].value<MatchingMode>();
            }
            else
            {
                mc_mode = infomap[runningKey]["matching-mode"].last().value<MatchingMode>();
            }
            if (_f.isDir()){
                std::string abspath = _f.absoluteFilePath().toStdString();
                fd_pathloop(QMultiByteToWideChar(abspath.data()),
                            infomap[runningKey]["matching-keyword"],
                            infomap[runningKey]["matching-suffix"],
                            mc_mode,
                            fishhook);
            } else if (_f.isFile()){
                std::string FilePath = _f.canonicalPath().toStdString();
                fd_pathloop(QMultiByteToWideChar(FilePath.data()),
                            infomap[runningKey]["matching-keyword"],
                            infomap[runningKey]["matching-suffix"],
                            mc_mode,
                            fishhook);
            }
            count++;
        }
        result.insert(runningKey, fishhook);
    }
    else if (funcType == FUNCTION_GZIP)
    {
        int index = 0;
        QList<QVariant> paths = infomap["zip-g"]["paths"];
        QList<QVariant> dirs = infomap["zip-g"]["dir"];
        QList<QVariant> filtervar = infomap["zip-g"]["filter"];
        QList<QString> filterstr;
        foreach(QVariant i, filtervar) filterstr.append(i.toString());
        foreach(QVariant i, paths)
        {
            QString dfdir = NULL;
            if (dirs.size() > index) dfdir = dirs[index].toString();
            ZipGet(i.toString(), dfdir, filterstr);
            index++;
        }
    }
    else if (funcType == FUNCTION_PZIP)
    {
        int index = 0;
        QList<QVariant> paths = infomap["zip-p"]["paths"];
        QList<QVariant> dirs = infomap["zip-p"]["dir"];
        foreach(QVariant i, paths)
        {
            QString dfdir = NULL;
            if (dirs.size() > index) dfdir = dirs[index].toString();
            ZipPush(i.toString(), dfdir);
            index++;
        }
    }
    else if (funcType == FUNCTION_MKPATH)
    {
        foreach(QVariant i, infomap["make-path"]["paths"])
        {
            makeSignalFile(i.toString());
        }
    }
    else if (funcType == FUNCTION_MKDIR)
    {
        int index = 0;
        foreach(QVariant i, infomap["make-dir"]["paths"])
        {
            bool rmlast = false;
            if (infomap["make-dir"]["rmlast"].size() > index) rmlast = infomap["make-dir"]["rmlast"][index].toBool();
            makeDirectory(i.toString(), rmlast);
        }
    }
    lock.unlock();
    quit();
}

void Nlwingsing::start(QString lockey, QThread::Priority priority){
    QThread::start(priority);
    runningKey.swap(lockey);
}

QList<QString> Nlwingsing::captrueData(QString lockey){
    wait_p();
    QList<QString> webdata = result[lockey];
    result[lockey].clear();
    return webdata;
}

void Nlwingsing::wait_p(){
    if (!isFinished())
    {
        QEventLoop loop;
        connect(this, &Nlwingsing::finished, &loop, &QEventLoop::quit);
        loop.exec();
    }
}

void Nlwingsing::captrueData(QString lockey, QMap<QString, QString> &data){
    QList<QString> datas = captrueData(lockey);
    foreach(QString info, datas)
    {
        QStringList infos = info.split("<->");
        QString key = infos.first();
        QString value = infos.last();
        data[key] = value;
    }
}

void Nlwingsing::captrueData(QString lockey, QStringList &data){
    QList<QString> datas = captrueData(lockey);
    if (!datas.isEmpty()) data.swap(datas);
}

void Nlwingsing::captrueData(QString lockey, QString &data){
    QList<QString> datas = captrueData(lockey);
    if (!datas.isEmpty()) data = datas.first();
}

void Nlwingsing::makeDirectory(QString directory, bool rmlast){
    QRegularExpression pathExpr("[a-zA-Z:\\.]*[\\\\|/]{1}");
    QRegularExpressionMatch disk = pathExpr.match(directory);
    QStringList cap = disk.capturedTexts();
    if (!cap.isEmpty()){
        QString simplepath = cap[0];
        QStringList splitdir = directory.mid(simplepath.size(), -1).split(QRegularExpression("\\\\|/"));
        if (rmlast) splitdir.removeLast();
        foreach(QString dir, splitdir) {
            //#依次判断 SimplePath 是否存在，从盘符开始，依次累积目录
            if (QFile(simplepath).exists()){
                if (!dir.isEmpty()) QDir(simplepath).mkpath(dir);
                if (simplepath.lastIndexOf(QRegularExpression("\\\\|/")) == simplepath.size() - 1)
                {
                    simplepath += dir;
                }
                else
                {
                    simplepath += "\\" + dir;
                }
            }
        }
    }
}

void Nlwingsing::makeSignalFile(QString file){
    int SpecialCharC = file.count(QRegularExpression("[\\\\|/]"));
    if (SpecialCharC > 1)
    {
        QString CropFile = file.mid(0, file.lastIndexOf(QRegularExpression("[\\\\|/]"), -1));
        QFile _f(file);
        if (QDir(CropFile).exists()){
            _f.open(QFile::WriteOnly);
        } else {
            makeDirectory(CropFile);
            _f.open(QFile::WriteOnly);
        }
        _f.close();
    }
}

void Nlwingsing::ZipGet(QString zpath, QString dir, QList<QString> filter){
    QZipReader  zip(zpath);
    QString simple_savepath=NULL;
    for (int i = 0; i < zip.count(); i++){
        QString zdatapath = zip.entryInfoAt(i).filePath;
        QString bscname = QFileInfo(zdatapath).baseName();
        if (dir.isNull()){
            simple_savepath = QFileInfo(zpath).canonicalPath() + "/" + zdatapath;
        } else {
            if (QFile(dir).exists()){
                simple_savepath = dir + "/" + zdatapath;
            } else {
                simple_savepath = QFileInfo(zpath).canonicalPath() + "/" + zdatapath;
            }
        }

        if (!filter.isEmpty()){
            foreach(QString Fitem , filter){
               if (bscname.contains(Fitem, Qt::CaseInsensitive)){
                    QString folder_create;
                    if (zip.fileData(zdatapath).isEmpty()){
                        continue;
                    } else {
                        folder_create = jigsawPath(simple_savepath, false);
                        makeDirectory(folder_create);
                        QFile flterfile(jigsawPath(simple_savepath));
                        flterfile.open(QIODevice::WriteOnly);
                        flterfile.write(zip.fileData(zdatapath));
                        flterfile.close();
                    }
               } else continue;
            }
        } else {
            QString folder_create;
            if (zip.fileData(zdatapath).isEmpty()){
                continue;
            } else {
                folder_create = jigsawPath(simple_savepath, false);
                makeDirectory(folder_create);
                QFile noflterfile(jigsawPath(simple_savepath));
                noflterfile.open(QIODevice::WriteOnly);
                noflterfile.write(zip.fileData(zdatapath));
                noflterfile.close();
            }
        }
    }
}

void Nlwingsing::ZipPush(QString zpath, QString dir){
    QRegularExpression Tonic("[\\\\|/]");
    QStringList tarpath;
    QString zipbscname=NULL;
    QString savefolder=NULL;
    if (dir.isNull()){
        savefolder = QFileInfo(zpath).canonicalPath();
    } else {
        if (QFileInfo(dir).isFile()){
            savefolder = jigsawPath(dir, false);
        } else {
            Tonic.setPattern("[a-zA-Z]\\W");
            if (dir.indexOf(Tonic) != -1){
                QRegularExpression special("[:\\.\\\\|/]");
                QRegularExpression emptydelReg(".{1,}");
                QStringList SplitDir = dir.split(special).filter(emptydelReg);
                QString FirstDisk = SplitDir[0];
                SplitDir.removeFirst();
                SplitDir.insert(0, FirstDisk.append(":/"));
                savefolder = SplitDir.join("/");
            } else savefolder = QFileInfo(zpath).canonicalPath();
        }
        makeDirectory(savefolder);
    }
    if (QFileInfo(zpath).isDir()){
        SCROPED_LOCAL_FIND(zpath, NULL, NULL, MatchingMode_Simple, tarpath);
        zipbscname = zpath.split(Tonic).last();
    } else {
        zipbscname = QFileInfo(zpath).baseName();
        tarpath << zpath;
    }
    QString ZipName = jigsawPath(savefolder + "/" + zipbscname + ".zip");
    QZipWriter Zwrite(ZipName);
    foreach (QString Path, tarpath){
        QString TarDirectory = Path.mid(Path.indexOf(zipbscname), -1);
        QFile WriteFile(Path);
        WriteFile.open(QIODevice::ReadOnly);
        Zwrite.addFile(TarDirectory.toLocal8Bit(), WriteFile.readAll());
        WriteFile.close();
    }
    Zwrite.close();
}

QString Nlwingsing::jigsawPath(QString path,bool esuffix, bool simplify){
    QRegularExpression Tonic("[\\\\|/]");
    QStringList ignore("...");
    QStringList SpSplitFd = path.split(Tonic);
    QStringList replaceList = SpSplitFd;
    if (simplify)
    {
        if (QFileInfo(path).isFile() && SpSplitFd.size() >= 5)
        {
            SpSplitFd = SpSplitFd.mid(0, 2) + ignore + SpSplitFd.mid(SpSplitFd.size() - 2);
        }
        else
        {
            SpSplitFd = SpSplitFd.mid(0, 2) + ignore + SpSplitFd.mid(SpSplitFd.size() - 1);
        }
    }
    if (!esuffix) SpSplitFd.removeLast();
    QString JoinFD = SpSplitFd.filter(QRegularExpression(".{1,}")).join("/");
    return JoinFD;
}

void Nlwingsing::fd_pathloop(std::string path, QList<QVariant> keywords, QList<QVariant> suffixs, QVariant mode,  QList<QString> &fishhook, bool isvariant_fish){
    MatchingMode mc_mode = mode.value<MatchingMode>();
    intptr_t curfolderhandle = 0; //# 创建一个当前目录的句柄，用于检测文件是否存在，_findnext 函数检测该句柄时，返回值为0 则表面该路径还有数据
    struct _finddata_t detetor; //# 创建一个结构体，负责装载数据
    /*
      long _findfirst(
      const char *filespec, 指定了要搜索的文件模式。例如，"*.txt" 会匹配当前目录下所有扩展名为 .txt 的文件
      struct _finddata *fileinfo, 指向 _finddata 结构的指针，该结构用于接收找到的文件的信息。
      int attrib 指定文件的属性，用于过滤搜索结果。例如，_A_NORMAL 表示只搜索普通文件
                _A_NORMAL 或 0x00：普通文件，没有其他属性。
                _A_RDONLY 或 0x01：只读文件。
                _A_HIDDEN 或 0x02：隐藏文件。
                _A_SYSTEM 或 0x04：系统文件。
                _A_ARCH 或 0x20：归档属性，通常表示文件已被修改且需要备份或压缩
      );

        attrib：文件属性，例如是否为只读、隐藏、系统文件等。
        time_create：文件创建的时间戳。
        date_create：文件创建的日期。
        time_access：文件最后访问的时间戳。
        date_access：文件最后访问的日期。
        time_write：文件最后写入的时间戳。
        date_write：文件最后写入的日期。
        size：文件的大小，以字节为单位。
        name：文件的名称。
    */
    std::string datection_folder;
    struct stat statbuf;
    /*
     * int stat(const char *path, struct stat *buf); success 0 fail -1
     *
        st_dev: 设备标识符，表示文件所在的设备。
        st_ino: 文件的inode号（UNIX/Linux系统中的唯一标识符）。
        st_mode: 文件模式，表示文件类型和权限。
            S_IFMT 掩码用于提取文件类型。 *宏
            S_IFREG 表示普通文件。 *宏
            S_IFDIR 表示目录。 *宏
            S_IFCHR 表示字符设备文件。 *宏
            S_IFBLK 表示块设备文件。 *宏
            S_IFLNK 表示符号链接。 *宏
            S_IFIFO 表示命名管道。 *宏
            S_ISUID、S_ISGID、S_ISVTX 等表示特殊权限位。 *宏
        st_nlink: 链接数，即有多少硬链接指向这个文件。
        st_uid: 文件所有者的用户ID。
        st_gid: 文件所有者的组ID。
        st_rdev: 如果是设备文件，表示设备ID。
        st_size: 文件大小，以字节为单位。
        st_blksize: 最优的I/O块大小。
        st_blocks: 文件占用的块数。
        时间戳属性，通常有以下三个，表示不同的时间点：
        st_atime: 最后访问时间。
        st_mtime: 最后修改时间。
        st_ctime: 状态改变时间，这可能包括权限更改、属性修改等
    */
    datection_folder.assign(path).append("\\*");
    curfolderhandle =_findfirst(datection_folder.data(), &detetor);//获取查找句柄，当前查找第一个句柄
    if (curfolderhandle != -1)
    {
        do{
            const char *datection_name = detetor.name;
            datection_folder.assign(path).append("/").append(datection_name);
            if (stat(datection_folder.data(), &statbuf) == 0)
            {
                if (S_IFDIR == (statbuf.st_mode & S_IFMT)) //S_IFMT 为掩码，可提取 st_mode 中的类型
                {
                    if (strcmp(datection_name, ".") != 0 && strcmp(datection_name, "..") != 0)
                    {
                        datection_folder.assign(path).append("/").append(datection_name);
                        fd_pathloop(datection_folder, keywords, suffixs, mode, fishhook);
                    }
                } else {
                    //创建fileinfo对象，用于检索后缀
                    QFileInfo fileinfo = QFileInfo(datection_name);
                    QString suffix;
                    if (fileinfo.suffix().isEmpty())
                    {
                        suffix = "";
                    } else {
                        suffix = fileinfo.suffix();
                    }
                    std::string temporary;
                    // _findfirst 是window 检索本地文件的 API，返回的是window的宽字符，需要转换为多位字符，才能被程序编码识别为路径
                    std::string realpath = QWideCharToMultiByte(temporary.assign(path).append("/").append(datection_name));
                    if (!suffixs.isEmpty())
                    {
                        for (auto index = 0; index < suffixs.size(); index++)
                        {
                            //如果后缀不为空，参数后缀与文件后缀不匹配大小写情况下一致
                            if (!suffix.isEmpty() && suffix.toLower() == suffixs[index].toString().toLower())
                            {
                                //如果关键字检索不为空
                                if (!keywords.isEmpty()){
                                    //检索关键字
                                    mc_keyword(realpath, keywords, mc_mode, fishhook, isvariant_fish);
                                } else {
                                    //如果为空，则通过后缀校验就添加该数据
                                    fishhook.push_back(realpath.data());

                                }
                            //如果文件名后缀为空
                            } else {
                                //如果后缀参数也为空，则匹配无后缀的文件名数据，加载
                                if (suffixs[index].toString().trimmed().isEmpty()){
                                    fishhook.push_back(realpath.data());
                                } else {
                                    //后缀不为空，则不与后缀为空的文件名匹配。跳下一个后缀匹配
                                    continue;
                                }
                            }
                        }
                    } else {
                        //如果关键字检索不为空
                        if (!keywords.isEmpty())
                        {
                            //检测关键字
                            mc_keyword(realpath, keywords, mc_mode, fishhook, isvariant_fish);
                        } else {
                           //关键字和后缀都为空，则直接添加文件
                           fishhook.push_back(realpath.data());
                        }
                    }
                }
            }
        } while (_findnext(curfolderhandle, &detetor) == 0); // _findnext 成功返回0 否则 -1，如果存在下一个句柄，说明还有文件可获取
    }
    _findclose(curfolderhandle);
}

void Nlwingsing::mc_keyword(std::string _rnwpath, QList<QVariant> kws, MatchingMode mode, QList<QString> &fishhook, bool isvariant_fish){
    QString basename = QFileInfo(_rnwpath.data()).fileName();
    foreach (QVariant keyw, kws){
        int spcount = 0;
        int from = 0;
        QList<int> spindex;
        QString keywchar = keyw.toString();
        if (mode == MatchingMode_Simple){ //匹配模式非顺序匹配
            for (auto kw = 0; kw < keywchar.size(); kw++)
            {
                //#每个字符包含的情况
                int singleindex = basename.indexOf(keywchar[kw], from, Qt::CaseInsensitive);
                if (singleindex != -1 && spindex.count(singleindex) == 0)
                { //如果索引不为 -1，包含于文件名中，并且不包含索引已被spindex包含，即索引不是重复的
                    spindex.append(singleindex); //加载索引
                    from = singleindex + 1; //在当前位基础上再进一位，重新匹配字符包含
                    spcount += 1; //检索字符匹配次数
                }
            }
            //如果在不关注顺序情况下字符匹配次数与关键字一致，加载该文件名数据
            if (spcount == keywchar.size()){
                if (isvariant_fish)
                {
                    fishhook.push_back(keywchar + "<->" + _rnwpath.data());
                }
                else
                {
                    fishhook.push_back(_rnwpath.data());
                }
            }

        }
        else if (mode == MatchingMode_Exact)
        {//大小写匹配模式
            //如果关键字在大小写匹配的情况下存在于文件名中，则加载该文件名
            if (basename.indexOf(keywchar, Qt::CaseInsensitive) == 0){
                if (isvariant_fish)
                {
                    fishhook.push_back(keywchar + "<->" + _rnwpath.data());
                }
                else
                {
                    fishhook.push_back(_rnwpath.data());
                }
            }
        }
        else if (mode == MatchingMode_Similarity)
        {
            double similarity_n = property("similarity-num").toDouble();
            if (similarity(basename, keyw.toString()) >= similarity_n)
            {
                if (isvariant_fish)
                {
                    fishhook.push_back(keywchar + "<->" + _rnwpath.data());
                }
                else
                {
                    fishhook.push_back(_rnwpath.data());
                }
            }
        }
    }
}

const std::string Nlwingsing::QMultiByteToWideChar(const char *bcc){
   std::stringstream _slot;
    if (bcc && strlen(bcc) > 1){
        int BscLenght = MultiByteToWideChar(CP_UTF8, 0, bcc, -1, NULL, 0);
        wchar_t *BscMapWc = new wchar_t[BscLenght + 1];
        memset(BscMapWc, 0, BscLenght + 1);
        MultiByteToWideChar(CP_UTF8, 0, bcc, -1, BscMapWc, BscLenght);
        int WcLenght = WideCharToMultiByte(CP_ACP, 0, BscMapWc, -1, NULL, 0, NULL, NULL);
        char *ByteC = new char[WcLenght + 1];
        memset(ByteC, 0, WcLenght + 1);
        WideCharToMultiByte(CP_ACP, 0, BscMapWc, -1, ByteC, WcLenght, NULL, NULL);
        _slot << ByteC;
        delete []BscMapWc;
        delete []ByteC;
        return _slot.str();
    } else {
        return "";
    }
};

const std::string Nlwingsing::QWideCharToMultiByte(const std::string gbc){
    std::stringstream _slot;
    if (gbc.data() && strlen(gbc.data()) > 1){
        //关键，字符长度计算为真是长度，不包括结尾\0符，因此初始化时要加上结尾长度+1，否则隐性报错
        int BscLenght = MultiByteToWideChar(CP_ACP, 0, gbc.data(), -1, NULL, 0);
        wchar_t *BscMapWc = new wchar_t[BscLenght + 1];
        memset(BscMapWc, 0, BscLenght + 1);
        MultiByteToWideChar(CP_ACP, 0, gbc.data(), -1, BscMapWc, BscLenght);

        int WcLenght = WideCharToMultiByte(CP_UTF8, 0, BscMapWc, -1, NULL, 0, NULL, NULL);
        char *ByteC = new char[WcLenght + 1];
        memset(ByteC, 0, WcLenght + 1);
        WideCharToMultiByte(CP_UTF8, 0, BscMapWc, -1, ByteC, WcLenght, NULL, NULL);

        _slot << ByteC;
        delete []BscMapWc;
        delete []ByteC;
        return _slot.str();
    }else{
        return "";
    }
}




